/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cj_ability_stage.h"
#include "cj_ability_stage_context.h"
#include "cj_runtime.h"
#include "context.h"
#include "hilog.h"
#include "securec.h"

// namespace {
// [TODO] duplicates with definition in 'cj_utils_ffi.cpp'
// char* CreateCStringFromString(const std::string& source)
// {
//     if (source.size() == 0) {
//         return nullptr;
//     }
//     size_t length = source.size() + 1;
//     auto res = static_cast<char*>(malloc(length));
//     if (res == nullptr) {
//         HILOG_ERROR("fail to mallc string.");
//         return nullptr;
//     }
//     if (strcpy_s(res, length, source.c_str()) != 0) {
//         free(res);
//         HILOG_ERROR("fail to strcpy source.");
//         return nullptr;
//     }
//     return res;
// }
// }

extern "C" {
CJ_EXPORT CurrentHapModuleInfo* FFICJCurrentHapModuleInfo(int64_t id)
{
    auto abilityStageContext = Keels::FFI::FFIData::GetData<Keels::AbilityRuntime::Platform::CJAbilityStageContext>(id);
    if (abilityStageContext == nullptr) {
        HILOG_ERROR("Get abilityStageContext failed. ");
        return nullptr;
    }

    auto hapInfo = abilityStageContext->GetHapModuleInfo();
    if (hapInfo == nullptr) {
        HILOG_ERROR("CurrentHapMoudleInfo is nullptr.");
        return nullptr;
    }

    CurrentHapModuleInfo* buffer = static_cast<CurrentHapModuleInfo*>(malloc(sizeof(CurrentHapModuleInfo)));

    if (buffer == nullptr) {
        HILOG_ERROR("Create CurrentHapMoudleInfo failed, CurrentHapMoudleInfo is nullptr.");
        return nullptr;
    }

    buffer->name = CreateCStringFromString(hapInfo->name);
    buffer->icon = CreateCStringFromString(hapInfo->iconPath);
    buffer->iconId = hapInfo->iconId;
    buffer->label = CreateCStringFromString(hapInfo->label);
    buffer->labelId = hapInfo->labelId;
    buffer->description = CreateCStringFromString(hapInfo->description);
    buffer->descriptionId = hapInfo->descriptionId;
    buffer->mainElementName = CreateCStringFromString(hapInfo->mainElementName);
    buffer->installationFree = hapInfo->installationFree;
    buffer->hashValue = CreateCStringFromString(hapInfo->hashValue);

    return buffer;
}

CJ_EXPORT Keels::AbilityRuntime::Platform::RetHapModuleInfoV2 FFICJGetHapModuleInfo(int64_t id)
{
    auto abilityStageContext = Keels::FFI::FFIData::GetData<Keels::AbilityRuntime::Platform::CJAbilityStageContext>(id);
    if (abilityStageContext == nullptr) {
        HILOG_ERROR("get abilityStageContext failed");
        return Keels::AbilityRuntime::Platform::RetHapModuleInfoV2();
    }

    return abilityStageContext->GetRetHapModuleInfo();
}

CJ_EXPORT Keels::AbilityRuntime::Platform::CConfiguration FFICJGetConfiguration(int64_t id)
{
    auto abilityStageContext = Keels::FFI::FFIData::GetData<Keels::AbilityRuntime::Platform::CJAbilityStageContext>(id);
    if (abilityStageContext == nullptr) {
        HILOG_ERROR("Get abilityStageContext failed. ");
        return Keels::AbilityRuntime::Platform::CConfiguration();
    }

    return abilityStageContext->GetConfiguration();
}

CJ_EXPORT int64_t FFIAbilityGetAbilityStageContext(AbilityStageHandle abilityStageHandle)
{
    auto ability = static_cast<Keels::AbilityRuntime::Platform::CJAbilityStage*>(abilityStageHandle);
    auto context = ability->GetContext();
    if (context == nullptr) {
        HILOG_ERROR("GetAbilityStageContext failed, abilityContext is nullptr.");
        return ERR_INVALID_INSTANCE_CODE;
    }
    auto cjStageContext = Keels::FFI::FFIData::Create<Keels::AbilityRuntime::Platform::CJAbilityStageContext>(context);
    if (cjStageContext == nullptr) {
        HILOG_ERROR("GetAbilityStageContext failed, abilityContext is nullptr.");
        return ERR_INVALID_INSTANCE_CODE;
    }
    return cjStageContext->GetID();
}
}

namespace Keels {
namespace AbilityRuntime {
namespace Platform {
std::shared_ptr<CJAbilityStage> CJAbilityStage::Create(
    const std::unique_ptr<Runtime>& runtime, const AppExecFwk::HapModuleInfo& hapModuleInfo)
{
    if (!runtime) {
        HILOG_ERROR("Runtime does not exist.");
        return nullptr;
    }
    auto& cjRuntime = static_cast<CJRuntime&>(*runtime);
    // Load cj app library.
    if (!cjRuntime.IsAppLibLoaded()) {
        HILOG_ERROR("Failed to create CJAbilityStage, applib not loaded.");
        return nullptr;
    }

    auto cjAbilityStageObject = CJAbilityStageObject::LoadModule(hapModuleInfo.moduleName);
    if (cjAbilityStageObject == nullptr) {
        cjRuntime.UnLoadCJAppLibrary();
        HILOG_ERROR("Failed to create CJAbilityStage.");
        return nullptr;
    }

    return std::make_shared<CJAbilityStage>(cjAbilityStageObject);
}

void CJAbilityStage::Init(const std::shared_ptr<Context>& context)
{
    AbilityStage::Init(context);
    if (!cjAbilityStageObject_) {
        HILOG_ERROR("Failed to create CJAbilityStage.");
        return;
    }
    cjAbilityStageObject_->Init(this);
}

void CJAbilityStage::OnCreate() const
{
    AbilityStage::OnCreate();
    if (!cjAbilityStageObject_) {
        HILOG_ERROR("CJAbilityStage is not loaded.");
        return;
    }
    HILOG_DEBUG("CJAbilityStage::OnCreate");
    cjAbilityStageObject_->OnCreate();
}

// std::string CJAbilityStage::OnAcceptWant(const AAFwk::Want& want)
// {
//     AbilityStage::OnAcceptWant(want);
//     if (!cjAbilityStageObject_) {
//         HILOG_ERROR("CJAbilityStage is not loaded.");
//         return "";
//     }
//     return cjAbilityStageObject_->OnAcceptWant(want);
// }

// std::string CJAbilityStage::OnNewProcessRequest(const AAFwk::Want& want)
// {
//     AbilityStage::OnNewProcessRequest(want);
//     if (!cjAbilityStageObject_) {
//         TAG_LOGE(AAFwkTag::APPKIT, "CJAbilityStage is not loaded.");
//         return "";
//     }
//     return cjAbilityStageObject_->OnNewProcessRequest(want);
// }

void CJAbilityStage::OnConfigurationUpdate(const Configuration& configuration)
{
    AbilityStage::OnConfigurationUpdate(configuration);
    auto fullConfig = GetContext()->GetConfiguration();
    if (!fullConfig) {
        HILOG_ERROR("configuration is nullptr.");
        return;
    }

    if (!cjAbilityStageObject_) {
        HILOG_ERROR("CJAbilityStage is not loaded.");
        return;
    }
    cjAbilityStageObject_->OnConfigurationUpdated(fullConfig);
}

// void CJAbilityStage::OnMemoryLevel(int level)
// {
//     AbilityStage::OnMemoryLevel(level);
//     if (!cjAbilityStageObject_) {
//         HILOG_ERROR("CJAbilityStage is not loaded.");
//         return;
//     }
//     cjAbilityStageObject_->OnMemoryLevel(level);
// }

void CJAbilityStage::OnDestroy() const
{
    AbilityStage::OnDestroy();
    if (!cjAbilityStageObject_) {
        HILOG_ERROR("CJAbilityStage is not loaded.");
        return;
    }
    cjAbilityStageObject_->OnDestroy();
}

// std::shared_ptr<Keels::AppExecFwk::CJDelegatorAbilityStageProperty> CJAbilityStage::CreateStageProperty() const
// {
//     auto property = std::make_shared<Keels::AppExecFwk::CJDelegatorAbilityStageProperty>();
//     auto context = GetContext();
//     if (!context) {
//         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get context");
//         return nullptr;
//     }
//     auto hapModuleInfo = context->GetHapModuleInfo();
//     if (!hapModuleInfo) {
//         TAG_LOGE(AAFwkTag::APPKIT, "Failed to get hapModuleInfo");
//         return nullptr;
//     }
//     property->moduleName_ = hapModuleInfo->name;
//     property->srcEntrance_ = hapModuleInfo->srcEntrance;
//     property->cjStageObject_ = cjAbilityStageObject_->GetId();
//     return property;
// }
} // namespace Platform
} // namespace AbilityRuntime
} // namespace Keels